package com.wangijun.yuan.web.api;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.wangijun.yuan.web.base.CommonPage;
import com.wangijun.yuan.web.base.CommonResult;
import com.wangijun.yuan.core.base.YesNoEnum;
import com.wangijun.yuan.core.domain.ChatInfo;
import com.wangijun.yuan.core.domain.ChatUser;
import com.wangijun.yuan.core.domain.UserInfo;
import com.wangijun.yuan.core.model.IdParam;
import com.wangijun.yuan.core.model.em.UserStatusEnum;
import com.wangijun.yuan.web.mapper.ChatInfoMapper;
import com.wangijun.yuan.web.mapper.ChatUserMapper;
import com.wangijun.yuan.web.model.dto.ChatInfoParam;
import com.wangijun.yuan.web.model.dto.ChatSelectUserParam;
import com.wangijun.yuan.web.model.dto.ChatSendParam;
import com.wangijun.yuan.web.model.dto.ChatUserParam;
import com.wangijun.yuan.web.model.vo.BaseUserVO;
import com.wangijun.yuan.web.model.vo.ChatInfoVO;
import com.wangijun.yuan.web.model.vo.ChatUserVO;
import com.wangijun.yuan.web.service.UserService;
import com.wangijun.yuan.web.socket.MsgVO;
import com.wangijun.yuan.web.socket.WebSocketService;
import com.wangijun.yuan.web.util.YuanWebUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;


@Tag(name = "B-私信相关接口")
@SaCheckLogin
@RestController
@RequiredArgsConstructor
@RequestMapping("/chat")
public class ChatController {
    private final UserService userService;
    private final ChatUserMapper chatUserMapper;
    private final ChatInfoMapper chatInfoMapper;


    @Operation(summary = "分页查询私信用户列表")
    @PostMapping("/user")
    CommonResult<CommonPage<ChatUserVO>> chatUser(@RequestBody ChatUserParam param) {

        LambdaQueryChainWrapper<ChatUser> wrapper = Db.lambdaQuery(ChatUser.class)
                .and(w -> w
                        .eq(ChatUser::getSendUserId, StpUtil.getLoginIdAsLong())
                        .or()
                        .eq(ChatUser::getReceiveUserId, StpUtil.getLoginIdAsLong()));
        if (StrUtil.isNotBlank(param.getQuery())) {
            List<Long> idList = userService.lambdaQuery()
                    .like(UserInfo::getUsername, param.getQuery())
                    .eq(UserInfo::getUserStatus, UserStatusEnum.normal.getCode())
                    .list()
                    .stream().map(UserInfo::getId).toList();
            if (!idList.isEmpty()) {
                wrapper.and(w -> w
                        .in(ChatUser::getSendUserId, idList)
                        .or()
                        .in(ChatUser::getReceiveUserId, idList));
            } else {
                wrapper.eq(ChatUser::getId, -1L);
            }
        }
        Page<ChatUser> page = wrapper.orderByDesc(ChatUser::getLastSendTime)
                .page(param.page());
        return CommonResult.data(CommonPage.toPage(page, ChatUserVO.class, this::handleCharUserVO));
    }

    /**
     * 功能描述: 处理ChatUserVO
     *
     * @author wlf 2024/8/15 17:28
     */
    private void handleCharUserVO(ChatUserVO vo) {
        vo.setChatUser(userService.getBaseUser(vo.getSendUserId().equals(StpUtil.getLoginIdAsLong())
                ? vo.getReceiveUserId()
                : vo.getSendUserId()));
        if (vo.getLastSendTime() == null) {
            vo.setRelativeTime("");
        } else {
            vo.setRelativeTime(YuanWebUtil.dateRelative(vo.getLastSendTime()));
        }

        int i = chatInfoMapper.hasChatInfo(vo.getSendUserId().equals(StpUtil.getLoginIdAsLong())
                ? vo.getReceiveUserId()
                : vo.getSendUserId(), StpUtil.getLoginIdAsLong());
        vo.setShowRed(i > 0);
    }

    @Operation(summary = "分页查询私信信息列表")
    @PostMapping("/info")
    CommonResult<CommonPage<ChatInfoVO>> chatInfo(@RequestBody ChatInfoParam param) {
        Page<ChatInfo> page = Db.lambdaQuery(ChatInfo.class)
                .and(w -> w
                        .eq(ChatInfo::getSendUserId, StpUtil.getLoginIdAsLong())
                        .eq(ChatInfo::getReceiveUserId, param.getReceiveUserId())
                )
                .or(w -> w
                        .eq(ChatInfo::getReceiveUserId, StpUtil.getLoginIdAsLong())
                        .eq(ChatInfo::getSendUserId, param.getReceiveUserId())
                )
                .orderByDesc(ChatInfo::getCreateTime).page(param.page());
        List<ChatInfo> records = page.getRecords();
        Collections.reverse(records);
        page.setRecords(records);
        return CommonResult.data(CommonPage.toPage(page, ChatInfoVO.class, (vo) -> {
            vo.setSendUser(userService.getBaseUser(vo.getSendUserId()));
            vo.setReceiveUser(userService.getBaseUser(vo.getReceiveUserId()));
        }));
    }

    @Operation(summary = "发送信息")
    @PostMapping("/send")
    CommonResult<ChatInfoVO> chatSend(@RequestBody @Validated ChatSendParam param) {
        ChatInfo chatInfo = new ChatInfo();
        chatInfo.setId(IdWorker.getId());
        chatInfo.setMsg(param.getMsg());
        chatInfo.setSendUserId(StpUtil.getLoginIdAsLong());
        chatInfo.setReceiveUserId(param.getReceiveUserId());
        chatInfo.setCreateTime(LocalDateTime.now());
        Db.save(chatInfo);
        ChatInfoVO vo = BeanUtil.copyProperties(chatInfo, ChatInfoVO.class);
        vo.setSendUser(userService.getBaseUser(vo.getSendUserId()));
        vo.setReceiveUser(userService.getBaseUser(vo.getReceiveUserId()));
        chatUserMapper.updateLastMsg(StpUtil.getLoginIdAsLong(), param.getReceiveUserId(),
                param.getMsg(), LocalDateTime.now());
        WebSocketService.sendMsg(param.getReceiveUserId(), new MsgVO(MsgVO.MsgType.refreshChat, vo));
        return CommonResult.data(vo);
    }

    @Schema(description = "已读消息参数")
    record ChatFlagParam(
            @NotNull(message = "参数不可为空")
            @Schema(description = "用户ID")
            Long userId) {
    }

    @Operation(summary = "已读消息通知")
    @PostMapping("/flag")
    CommonResult<Boolean> chatFlag(@RequestBody @Validated ChatFlagParam param) {
        if (Db.lambdaQuery(ChatInfo.class)
                .eq(ChatInfo::getReadFlag, YesNoEnum.NO.getCode())
                .eq(ChatInfo::getSendUserId, param.userId())
                .eq(ChatInfo::getReceiveUserId, StpUtil.getLoginIdAsLong())
                .count() > 0) {
            Db.lambdaUpdate(ChatInfo.class)
                    .set(ChatInfo::getReadFlag, YesNoEnum.YES.getCode())
                    .eq(ChatInfo::getSendUserId, param.userId())
                    .eq(ChatInfo::getReceiveUserId, StpUtil.getLoginIdAsLong())
                    .update();
        }

        return CommonResult.data(true);
    }

    @Operation(summary = "是否有未读通知，true有通知")
    @PostMapping("/tag")
    CommonResult<Boolean> chatTag() {
        ChatInfo one = Db.lambdaQuery(ChatInfo.class)
                .eq(ChatInfo::getReadFlag, YesNoEnum.NO.getCode())
                .eq(ChatInfo::getReceiveUserId, StpUtil.getLoginIdAsLong())
                .last("limit 1").one();
        return CommonResult.data(one != null);
    }


    @Schema(description = "已读消息参数")
    record ChatNewParam(
            @NotNull(message = "参数不可为空")
            @Schema(description = "用户ID")
            Long userId) {
    }

    @Operation(summary = "发起新会话")
    @PostMapping("/new")
    CommonResult<ChatUserVO> chatNew(@RequestBody @Validated ChatNewParam param) {
        // 判断是否已经存在，已经存在则不写入
        ChatUser one = Db.lambdaQuery(ChatUser.class)
                .and(w ->
                        w.eq(ChatUser::getSendUserId, StpUtil.getLoginIdAsLong())
                                .eq(ChatUser::getReceiveUserId, param.userId))
                .or(w ->
                        w.eq(ChatUser::getSendUserId, param.userId)
                                .eq(ChatUser::getReceiveUserId, StpUtil.getLoginIdAsLong()))
                .one();
        ChatUserVO vo;
        if (one == null) {
            ChatUser chatUser = new ChatUser();
            chatUser.setId(IdWorker.getId());
            chatUser.setSendUserId(StpUtil.getLoginIdAsLong());
            chatUser.setReceiveUserId(param.userId);
            Db.save(chatUser);
            vo = BeanUtil.copyProperties(chatUser, ChatUserVO.class);
            vo.setShowRed(false);
            vo.setChatUser(userService.getBaseUser(param.userId));
        } else {
            vo = BeanUtil.copyProperties(one, ChatUserVO.class);
            handleCharUserVO(vo);
        }
        if (vo.getLastSendTime() == null) {
            vo.setRelativeTime("");
        } else {
            vo.setRelativeTime(YuanWebUtil.dateRelative(vo.getLastSendTime()));
        }
        return CommonResult.data(vo);
    }


    @Operation(summary = "删除会话信息")
    @PostMapping("/del")
    CommonResult<Boolean> chatDel(@RequestBody @Validated IdParam param) {
        Db.removeById(param.getId(),ChatUser.class);
        return CommonResult.data(true);
    }


    @Operation(summary = "分页查询用户信息")
    @PostMapping("/select-user")
    CommonResult<CommonPage<BaseUserVO>> chatSelectUser(@RequestBody ChatSelectUserParam param) {
        Page<UserInfo> page = Db.lambdaQuery(UserInfo.class)
                .like(UserInfo::getUsername, param.getQuery())
                .ne(UserInfo::getId, StpUtil.getLoginIdAsLong())
                .orderByDesc(UserInfo::getUpdateTime).page(param.page());
        CommonPage<BaseUserVO> commonPage = CommonPage.toPage(page, BaseUserVO.class);
        commonPage.setRows(page.getRecords().stream().map(i ->
                new BaseUserVO(i.getId(), i.getUsername(), i.getAvatar(), i.getSummary())).toList()
        );
        return CommonResult.data(commonPage);
    }

}
